package com.library.guard.core;

import Tool.BankData;
import Tool.TagFilter;
import ZO_API.NO1_Api;
import ZO_API.TagReadDataEventCallback;
import com.library.books.service.IBooksCopyService;
import com.library.books.service.IDoorAlarmRecordService;
import com.library.common.constant.SysConfigConstants;
import com.library.common.core.redis.RedisCache;
import com.library.common.utils.spring.SpringUtils;
import com.library.framework.manager.AsyncManager;
import com.library.common.wspoint.point.GlobalServerPoint;
import com.library.guard.core.config.N01Config;
import com.library.guard.core.domain.AntSet;
import com.library.guard.core.domain.GpoSet;
import com.library.guard.func.GpoNoticeCallback;
import com.library.guard.func.TagCheckCallback;
import com.library.system.domain.SysConfig;
import com.library.system.service.ISysConfigService;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Package: com.library.guard.core
 * @ClassName: NZeroAndOne
 * @Author: Yin
 * @Date: 16:28
 * @Description: 对api的规范调整
 */
@Slf4j
public class ZeroAndOneApi extends NO1_Api {

    /* RET_ERRNO
     *
     * RET_OK(0),                   ---执行成功
     * RET_ERR_CONNECT(1),          --连接错误
     * ERR_LINK_FAIL(-1),           ---读写器未连接或连接已断开
     * ERR_BACK_DATA_FAIL(-2),      --返回信息错误，一般出现在返回信息时，是某个具体的操作执行失败，并不影响其他，重新发送即可
     * ERR_CONNECT_FAIL(-3);        ---连接错误，未知的连接方式
     * ERR_OTA_FAIL(-4);            ---OTA升级错误
     * N01_ERR_NOT_LICENSE(-5);     ---未授权
     * N01_ERR_LICENSE_FAIL(-6);    ---授权错误
     *
     *
     * Sleep
     *  对机器发送特定动作时，不弄延迟时间还不一定能成功
     */

    /** 对应位置GPO显示 */
    public final int[] show = new int[] {0,0,0,0};

    public final ReentrantLock ONE_LOCK = new ReentrantLock(false);

    public final Set<String> sets = new HashSet<>();

    public final Set<String> tids = new HashSet<>();

    public final Set<String> epcs = new HashSet<>();

    /** 异步盘存是否使用 */
    public final AtomicBoolean isStartReading = new AtomicBoolean(false);

    /** 是否连接 */
    public final AtomicBoolean isConn = new AtomicBoolean(false);

    /** 心跳包获取失败的结果 */
    private final String HEART_DOWN = "Disconnect";

    /** 机器读取标签数据回调函数 */
    private TagReadDataEventCallback asyncInvStartThreadCallback;

    /** 标签检查回调 */
    private TagCheckCallback tagCheckCallback;

    /** 设备ip */
    @Getter
    private String ip;

    @Getter
    private int port;

    /** 回调标签缓存 */
    @Getter
    private String callbackTagCacheKey;

    // 业务相关bean

    RedisCache redisCache;

    IBooksCopyService booksCopyService;

    IDoorAlarmRecordService doorAlarmRecordService;

    ISysConfigService configService;

    /** 全部报警 */
    GlobalServerPoint globalServerPoint;

    /// 2024-12 注释之前的
    public ZeroAndOneApi(String ip) {
        log.info("设备[{}]API创建", ip);
        this.ip = ip;
        this.port = 8080;
        this.callbackTagCacheKey = N01Config.DOOR_CALLBACK_TAG_SET_CACHE_KEY + ip;
        this.configService = SpringUtils.getBean(ISysConfigService.class);

        // 获取时区以实现监听心跳
        addListenHeart(listenHeart -> {
            String result = new String(listenHeart.getBytes(Charset.defaultCharset()));
            log.info("[{}]监听心跳包", ip);
            if (HEART_DOWN.equals(result)) {
                log.info("[{}]尝试重新连接", ip);
                isConn.set(false);
                try {
                    TimeUnit.MILLISECONDS.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (connection()) {
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    log.info("回调:{}", asyncInvStartThreadCallback);
                    log.info("[{}]重连成功，重新启动快速盘存[{}]", ip, startAsyncReading());
                }
            }
        });

        // 异步检查（check 方法）
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                log.info("设备[{}]缓存数据异步检查",ip);
                while(true) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(200);
                        if(tagCheckCallback != null && isConnected()) {
                             tagCheckCallback.check();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    /// 2024-12 注释之后的（移除了一些多余）
    /*public ZeroAndOneApi(String ip) {
        log.info("设备[{}]API创建",ip);
        this.ip = ip;
        this.port = 8080;
        this.callbackTagCacheKey = N01Config.DOOR_CALLBACK_TAG_SET_CACHE_KEY + ip;
        // 业务bean
        this.redisCache = SpringUtils.getBean(RedisCache.class);
        this.booksCopyService = SpringUtils.getBean(IBooksCopyService.class);
        this.doorAlarmRecordService = SpringUtils.getBean(IDoorAlarmRecordService.class);
        this.globalServerPoint = SpringUtils.getBean(GlobalServerPoint.class);

        // 获取时区以实现监听心跳
        addListenHeart(listenHeart -> {
            String result = new String(listenHeart.getBytes(Charset.defaultCharset()));
            log.info("[{}]监听心跳包",ip);
            if(HEART_DOWN.equals(result)) {
                log.info("[{}]尝试重新连接",ip);
                isConn.set(false);
                try {
                    TimeUnit.MILLISECONDS.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if(connection()) {
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    log.info("回调:{}",asyncInvStartThreadCallback);
                    log.info("[{}]重连成功，重新启动快速盘存[{}]",ip,startAsyncReading());
                }
            }
        });

        // 启动一个线程持续读取快速盘存回调的结果（EPC 对象）
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                try {
                    // 机器可能未启动，等待启动
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // 从缓存中获取数据检查 (数据由机器读取储存)
                try {
                    List setTags = (List) redisCache.redisTemplate.execute(RedisScript.of(N01Config.TAG_PULL_HASH_SCRIPT, List.class), List.of(getCallbackTagCacheKey()));

                    if(setTags != null && setTags.size() > 0) {
                        List<DeviceReportData.Epc> simpleEpcs = new ArrayList<>();
                        for (Object setTag : setTags) {
                            try {
                                DeviceReportData.Epc epc = JSON.parseObject(String.valueOf(setTag),DeviceReportData.Epc.class);
                                epc.setIp(ip);
                                simpleEpcs.add(epc);
                            }catch (Exception e) {
                                log.error("快速盘存缓存数据获取异常：{}",setTag);
                                e.printStackTrace();
                            }
                        }

                        // 创建一个检查数据
                        DeviceReportData deviceReportData = new DeviceReportData();
                        deviceReportData.setEpcs(simpleEpcs);
                        deviceReportData.setTagCount(setTags.size());
                        deviceReportData.setIp(ip);

                        DeviceReport deviceReport = new DeviceReport();
                        deviceReport.setData(deviceReportData);
                        deviceReport.setCmd("20001");
                        deviceReport.setIp(ip);

                        // 检查RFID
                        checks(deviceReport);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 初始化快速盘存回调函数设置
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                // 快速盘存回调操作（方法保存起来好作校验）
                setAsyncReadingCallback(tagBackData -> {
                    try {
                        Thread.sleep(200);
                    } catch (Exception e) {
                        log.error("快速盘存回调读取失败失败[超时异常]");
                        e.printStackTrace();
                    }
                    // 检查缓存中是否存在（手动处理重复标签）
                    String bankData = tagBackData.getBank_data();
                    if (bankData == null) {
                        return;
                    }

//                    tids.add(tagBackData.getBank_data());
//                    log.info("tids size:{}", tids.size());
//                    log.info("tids list:{}", tids);

//                    epcs.add(tagBackData.getEpc());
//                    log.info("epcs size:{}", epcs.size());
//                    log.info("epcs list:{}", epcs);

                    // 不存在时 Set
                    if (redisCache.hasKey(N01Config.DOOR_REDO_CACHE_KEY + bankData)) {
                        log.info("重复标签：{}", bankData);
                        return;
                    }
                    // 重复标签过期时间
                    redisCache.setCacheObject(N01Config.DOOR_REDO_CACHE_KEY + bankData, "1");
                    redisCache.expire(N01Config.DOOR_REDO_CACHE_KEY + bankData, N01Config.DOOR_REDO_CACHE_KEY_TIMEOUT, TimeUnit.SECONDS);
                    log.info("不重复标签：{}", bankData);

                    DeviceReportData.Epc epc = new DeviceReportData.Epc();
                    epc.setEpc(tagBackData.getEpc());
                    epc.setBankData(bankData);
                    epc.setAnt(tagBackData.getAntid());
                    epc.setRssi(tagBackData.getRssi());
                    epc.setReadCount(tagBackData.getCounts());

                    // 手动推入缓存中
                    redisCache.redisTemplate.opsForHash().put(getCallbackTagCacheKey(), bankData, epc);
                    // 开启盘存
                    log.info("智能门禁快速盘存初始开启：{}", startAsyncReading());
                });
            }
        });
    }*/

    /** 2024-12 注释之后的（移除了一些多余）
     * 检查方法（检查数据是否报警）
     * @param deviceReport 设备上报数据
     */
    /*public void checks(DeviceReport deviceReport) {
        DeviceReportData data = deviceReport.getData();
        // 以bank_data 为准
        List<DeviceReportData.Epc> epcs = data.getEpcs();
        if (StringUtils.isNotEmpty(epcs)) {
            // 以key为准 (可重复使用)
            Map<String, List<DeviceReportData.Epc>> epcMaps = epcs.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(DeviceReportData.Epc::getBankData));
            // 异步执行
            AsyncManager.me().execute(new TimerTask() {
                @Override
                public void run() {
                    try {
                        Set<String> epsStrList = epcMaps.keySet();
                        /// 原本的list
                        log.info("读到标签数:{}", epsStrList.size());
                        // 书籍拼接副本数据(软删除)
                        List<BooksJoinBooksCopy> booksCopies = booksCopyService.selectBooksCopyJoinBooksList(new BooksJoinBooksCopy().setQueryRfids(epsStrList));
                        if (! booksCopies.isEmpty()) {
                            // 红书列表
                            List<BooksJoinBooksCopy> isScanNormal = booksCopies.stream()
                                .filter(b -> BooksCopyStatus.NORMAL.equals(b.getBooksCopyStatus()))
                                .collect(Collectors.toList());
                            // 绿
                            boolean isScanLend = booksCopies.stream().anyMatch(b -> BooksCopyStatus.LEND.equals(b.getBooksCopyStatus()));
                            // 有未借出数据
                            log.info("未借:{},已借:{}", isScanNormal.size() > 0,isScanLend);
                            if (isScanNormal.size() > 0) {
                                log.info("GPO 1");
                                setGpo(GpoSet.red());
                                AsyncManager.me().execute(new TimerTask() {
                                    @Override
                                    public void run() {
                                        // 批量添加报警记录数据
                                        List<DoorAlarmRecord> alarmRecord = isScanNormal.stream()
                                            .map(scanNormal -> {
                                                DeviceReportData.Epc alarmEpc = epcMaps.get(scanNormal.getBooksCopyRfid()).get(0);
                                                    return new DoorAlarmRecord()
                                                        .setAlarmIp(alarmEpc.getIp())
                                                        .setAlarmAnt(alarmEpc.getAnt())
                                                        .setAlarmBankData(alarmEpc.getBankData())
                                                        .setBooksId(scanNormal.getBooksId())
                                                        .setBooksIsbn(scanNormal.getBooksIsbn())
                                                        .setBooksName(scanNormal.getBooksName())
                                                        .setBooksCoverPhoto(scanNormal.getBooksCoverPhoto())
                                                        .setBooksCopyLicense(scanNormal.getBooksCopyLicense()) ;
                                            }).collect(Collectors.toList());
                                        doorAlarmRecordService.saveBatch(alarmRecord);
                                        // 发送报警给系统
                                        AlarmWsData alarmWsData = new AlarmWsData();
                                        alarmWsData.setList(alarmRecord);
                                        globalServerPoint.send(alarmWsData);
                                    }
                                });
                                // 持续时间
                                Thread.sleep(1000);
                                setGpo(GpoSet.def());
                                log.info("GPO 2");
                            }else if(! isShow("1")) {
                                // 同步
                                if (ONE_LOCK.tryLock(500, TimeUnit.MILLISECONDS)) {
                                    try {
                                        sets.addAll(epsStrList);
                                        log.info("tags size:{}", sets.size());

                                        if (isScanLend) {
                                            setGpo(GpoSet.green());
                                            Thread.sleep(1000);
                                            if(isShow("2")) {
                                                setGpo(GpoSet.def());
                                            }
                                        }
                                    }finally {
                                        ONE_LOCK.unlock();
                                    }
                                }
                            }
                        }else {
                            log.info("非数据库RFID数据");
                        }
                    }catch (Exception e) {
                        log.error("扫描到未借书籍");
                    }
                }
            });
        }
    }*/

    public synchronized boolean setGpo(GpoSet gpoSet) {
        return setGpo(gpoSet,null);
    }

    public synchronized boolean setGpo(GpoSet gpoSet , GpoNoticeCallback callback) {
        try{
            if(gpoSet.getPin() != 0 && show[0] != 0) {
                show[0] =  0;
                Thread.sleep(150);
                this.N01_SetGpo(0,0);
            }
            if(gpoSet.getPin() != 1 && show[1] != 0) {
                show[1] =  0;
                Thread.sleep(150);
                this.N01_SetGpo(1,0);
            }
            if(gpoSet.getPin() != 2 && show[2] != 0) {
                show[2] =  0;
                Thread.sleep(150);
                this.N01_SetGpo(2,0);
            }
            if(gpoSet.getPin() != 3 && show[3] != 0) {
                show[3] =  0;
                Thread.sleep(150);
                this.N01_SetGpo(3,0);
            }
        }catch (Exception e) {e.printStackTrace();}
        // 灯状态更新
        show[gpoSet.getPin()] = gpoSet.getLevel();
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        boolean b = this.N01_SetGpo(gpoSet.getPin(), gpoSet.getLevel()).value() == 0;
        if(b) {
            if(callback != null) {
                callback.notice();
            }
            return true;
        }
        return false;
    }

    /**
     * 设置天线功率
     * @param antSet
     */
    public synchronized Boolean setAntPower(AntSet antSet) {
        try {
            Thread.sleep(500);
            this.stopAsyncReading();

            // 更新数据库config
            SysConfig select = new SysConfig();
            select.setConfigKey(ip + SysConfigConstants.DOOR_DRIVE_IP_ANT_PWR_CONNECT);
            SysConfig config =  configService.selectConfigList(select).stream().findFirst().orElse(null);
            if(config == null) {
                log.error("设备[{}]天线设置数据库参数空，尝试新增",ip);
                config = new SysConfig();
                config.setConfigId(null);
                config.setConfigName("设备["+ip+"]天线启动参数");
                config.setConfigType("Y");
                config.setConfigValue(String.format("%s,%s,%s,%s",antSet.getPower(),antSet.getPower(),antSet.getPower(),antSet.getPower()));
                configService.insertConfig(config);
            }else {
                String[] antPow = config.getConfigValue().split(",");
                antPow[antSet.getAnt() - 1 ] = String.valueOf(antSet.getPower());
                config.setConfigValue(String.join(",", antPow));
                configService.updateConfig(config);
            }
            Thread.sleep(500);
            this.startAsyncReading();
            log.info("设备[{}]天线功率设置成功[ant:{},pow:{}]",ip,antSet.getAnt(),antSet.getPower());
            return Boolean.TRUE;
        } catch (InterruptedException e) {
            log.error("设备[{}]天线功率设置失败[ant:{},pow:{}]",ip,antSet.getAnt(),antSet.getPower());
            return Boolean.FALSE;
        }
    }

    public void setTagCacheReadCheckCallback(TagCheckCallback tagCheckCallback) {
        // 处理检查数据回调
        this.tagCheckCallback = tagCheckCallback;
    }

    /** 设置快读盘存回调 */
    public void setAsyncReadingCallback(TagReadDataEventCallback callback) {
        // 设置回调，接收数据
        this.asyncInvStartThreadCallback = callback;
    }

    /**
     * 启用异步盘存
     *  两者只能调用其一
     *  - this.N01_StartReadingBank
     *  - this.N01_StartReading
     */
    public boolean startAsyncReading() {
        // 注意天线功率
        synchronized (isStartReading) {

            if(asyncInvStartThreadCallback == null) {
                log.error("快速盘存启动失败，盘存回调为空");
                return false;
            }

            /// 机器天线功率初始化（系统参数中提供）
            String antPwrStr = configService.selectConfigByKey(ip + SysConfigConstants.DOOR_DRIVE_IP_ANT_PWR_CONNECT );
            log.info("智能门禁设备[{}]快速盘存初始参数：{}",ip,antPwrStr);
            if(antPwrStr.isEmpty()) {
                log.error("智能门禁设备[{}]快速盘存初始参数：{}",ip,antPwrStr);
            }
            // 设备只有四个天线
            NO1_Api.N01AntPwr[] n01AntPwrs = new NO1_Api.N01AntPwr[4];
            String[] split = antPwrStr.split(",");
            for (int i = 0; i < split.length; i++) {
                N01AntPwr n01AntPwr = new N01AntPwr();
                int pwr = Integer.parseInt(split[i]);
                n01AntPwr.setAnt(i+1);
                n01AntPwr.setRead(pwr);
                n01AntPwr.setWrite(pwr);
                n01AntPwrs[i]=n01AntPwr;
            }
            log.info("智能门禁设备[{}]启动快速盘存使用天线功率：{}",ip,antPwrStr);
            this.N01_SetMultiAntPwr(n01AntPwrs);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 盘存回调
            this.AsyncInvStartThread(asyncInvStartThreadCallback);

            // 指定过滤的一些参数
            TagFilter tt = new TagFilter(2, 0, "", true);
            BankData bb = new BankData(2, 0, 6);
            // 启动快速盘存读取指定bank区数据&过滤
            RET_ERRNO isStart = this.N01_StartReadingBank(tt, bb);
            log.info("快速盘存启动：{}",isStart);

            isStartReading.set(true);
            return isStart.value() == 0;
        }
    }

    /** 停止异步盘存 */
    public boolean stopAsyncReading() {
        synchronized (isStartReading) {
            // 停止异步盘存
            RET_ERRNO isStop = this.N01_StopReading();
            log.info("智能门禁[异步盘存]停止：{}",isStop);
            isStartReading.set(false);
            return isStop.value() == 0;
        }
    }

    /**
     * 尝试连接
     */
    public synchronized boolean connection() {
        if(isConn.get()) {
            return true;
        }

        NO1_Api.RET_ERRNO retErrno = this.N01_Connect(N01Config.TCP_IP,ip);
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("智能门禁设备[{}]连接结果：{}",ip,retErrno.value());
        if(retErrno.value() != 0 && this.linksocket.link_socket == null) {
            log.error("智能门禁设备[{}]连接失败：[name:{},code:{}]",ip,retErrno.name(),retErrno.value());
            return false;
        }
        isConn.set(true);
        try {
            TimeUnit.MILLISECONDS.sleep(100);
            boolean setGpo0 = this.setGpo(GpoSet.def());
            TimeUnit.MILLISECONDS.sleep(100);
            boolean setGpo2 = this.setGpo(GpoSet.green());
            TimeUnit.MILLISECONDS.sleep(100);
            boolean setGpo00 = this.setGpo(GpoSet.def());
            log.info("===初始GPO设置：{},{},{}",setGpo0,setGpo2,setGpo00);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    /**
     * 释放连接
     */
    public synchronized void disconnect() {
        if(isConn.get()) {
            this.N01_Close();
        }
    }

    /** 是否连接上 */
    public boolean isConnected() {
        /// 并未置空
        // boolean isConn = this.linksocket.link_socket.isConnected();
        // boolean isConn = this.linksocket.link_socket != null;
        boolean isConn = this.isConn.get();
        log.info("智能门禁设备连接[{}]是否连接：{}",ip,isConn);
        return isConn;
    }

    /** 显示状态 */
    public synchronized boolean isShow(String index) {
        log.info("灯状态:{}",show);
        if("1".equals(index)) {
            return show[1] == 1;
        }else if("2".equals(index)) {
            return show[1] == 0 && show[2] == 1;
        }
        return false;
    }

}
